//
//  GGAppServiceManager.m
//  TestAppDelegateService
//
//  Created by yuemei on 2017/9/21.
//  Copyright © 2017年 yuemei. All rights reserved.
//

#import "GGAppServiceManager.h"
#import <objc/runtime.h>
#import <objc/message.h>
@interface GGAppServiceManager()
@property (nonatomic, strong) Class<UIApplicationDelegate> appDelegateClass;
@property (nonatomic, strong) NSMutableDictionary<NSString*, id<GGAppService>> *servicesMap;
@end


@implementation GGAppServiceManager

static GGAppServiceManager *shardManager = nil;
+(instancetype)sharedAppServiceManager{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        shardManager = [[GGAppServiceManager alloc] init];
    });
    
    return shardManager;
}
    
-(instancetype)init{
    if (self = [super init]) {
        self.servicesMap = [NSMutableDictionary dictionary];
    }
    return self;
}
    
-(void)registerService:(id<GGAppService>)service{
    if (!service) {
        return;
    }
    //如果通过类型检查,则将服务注册进ServiceMap进行待命.
    id<GGAppService> pre = self.servicesMap[[service serviceName]];
    if (pre) {
        if ([service isKindOfClass:[pre class]]) {
            self.servicesMap[[service serviceName]] = service;
        }else{
            NSAssert([pre isKindOfClass:[service class]],
                     @"尝试注册 %@ 和 %@ 作为 %@ 服务的处理程序. \
                     不能确定这两个类的类型正确性",
                     [pre class], [service class], [service serviceName]);
        }
    }else{
        self.servicesMap[[service serviceName]] = service;
    }
}
#pragma mark - 代理处理
-(BOOL)proxyCanResponseToSelector:(SEL)aSelector{
    
    __block IMP imp = NULL;
    
    [self.servicesMap enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, id<GGAppService>  _Nonnull obj, BOOL * _Nonnull stop) {
        if ([obj respondsToSelector:aSelector]) {
            imp = [(id)obj methodForSelector:aSelector];
            //验证方法,如果方法返回值长度大于0并且返回值类型为BOOL
            NSMethodSignature *signature = [(id)obj methodSignatureForSelector:aSelector];
            if (signature.methodReturnLength > 0 && strcmp(signature.methodReturnType, @encode(BOOL)) != 0) {
                imp = NULL;
            }
            *stop = YES;
        }
    }];
    
    return imp != NULL && imp != _objc_msgForward;
}

    
-(void)proxyForwardInvocation:(NSInvocation *)anInvocation{
    
    NSMethodSignature *signature = anInvocation.methodSignature;
    NSUInteger argCount = signature.numberOfArguments;
    __block BOOL returnValue = NO;
    NSUInteger returnLength = signature.methodReturnLength;
    void *returnValueBytes = NULL;
    //如果返回值长度大于0 则在栈上分配同等大小内存空间
    if (returnLength > 0) {
        returnValueBytes = alloca(returnLength);
    }
    [self.servicesMap enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, id<GGAppService>  _Nonnull obj, BOOL * _Nonnull stop) {
        
        if (![obj respondsToSelector:anInvocation.selector]) {
            return;
        }
        
        
        //容错,根据签名中参数的runtime内部编码拼接字符串,判断一致性
        NSAssert([[self objectTypeFromSignature:signature] isEqualToString:[self objectTypeFromSignature:[(id)obj methodSignatureForSelector:anInvocation.selector]]],@"selector-%@的方法签名%@-%@是无效的,请检查返回值类型和参数类型",NSStringFromSelector(anInvocation.selector),obj.serviceName,obj);
        //创建Invocation
        NSInvocation *invok = [NSInvocation invocationWithMethodSignature:signature];
        invok.selector = anInvocation.selector;
        
        //复制参数
        for (NSUInteger i = 0; i < argCount; ++i) {
            //获取参数类型
            const char * argType = [signature getArgumentTypeAtIndex:i];
            //参数大小
            NSUInteger argSize = 0;
            NSGetSizeAndAlignment(argType, &argSize, NULL);
            //根据参数大小分配内存空间
            void *argValue = alloca(argSize);
            //指定空间获取参数
            [anInvocation getArgument:&argValue atIndex:i];
            //将参数g根据对应index放进Invocation
            [invok setArgument:&argValue atIndex:i];
    }
        //设置消息转发目标
        invok.target = obj;
        //启动
        [invok invoke];
        //获取返回值
        if (returnValueBytes) {
            [invok setReturnValue:returnValueBytes];
            returnValue = returnValue || *((BOOL *)returnValueBytes);
        }
    }];
    //设置返回值
    if (returnValueBytes) {
        [anInvocation setReturnValue:returnValueBytes];
    }
}
    
    
-(NSString *)objectTypeFromSignature:(NSMethodSignature *)signature{
    //方法返回类型如果不存在标识为void
    NSMutableString *types = [NSMutableString stringWithFormat:@"%s",signature.methodReturnType?:"v"];
    //拼参数返回值字符串
    for (NSUInteger i = 0; i < signature.numberOfArguments;  ++i) {
        [types appendFormat:@"%s",[signature getArgumentTypeAtIndex:i]];
    }
    return [types copy];
}
@end
